//build: 20240715
//Data communication channels include serial port, USB (Universal Serial Bus), and Wiegand.
import { channelClass } from './libvbar-m-dxchannel.so'
import std from './dxStd.js'
import dxMap from './dxMap.js'
import dxCommon from './dxCommon.js'
import bus from './dxEventBus.js'
const uartObj = new channelClass();
const map = dxMap.get('default')
const uart = {}
uart.TYPE = {
	USBKBW: 1,//The USB Keyboard Wedge connects to the keyboard via a USB interface and transmits data using the Wiegand protocol.
	USBHID: 2,//USB Human Interface Device (USB) Channel Type
	UART: 3,//Indicates the UART channel type, i.e., serial port channel.
	WIEGAND: 4//Wiegand channel type
}

/* Enumeration of setting options for various channel IO control operations */
uart.IOC_SET_CMD = {
    /* Configure the KBW channel parameters */
    CHANNEL_IOC_SET_KBW_CONFIG      : 1,
    /* Configure the host computer parameters for the KBW channel. */
    CHANNEL_IOC_SET_KBW_UPPER       : 2,
    /* KBW Launch Time */
    CHANNEL_IOC_SET_KBW_UPTIME      : 3,
    /* KBW offline time */
    CHANNEL_IOC_SET_KBW_DOWNTIME    : 4,
    /* Set the report length for the HID channel */
    CHANNEL_IOC_SET_HID_REPORT_LEN  : 5,
    /* Configure UART channel parameters */
    CHANNEL_IOC_SET_UART_PARAM      : 6,
    /* Setting the working mode of the Wiegand channel */
    CHANNEL_IOC_SET_WIEGAND_MODE    : 7,
    /* Configure the Wiegand channel's GPIO. */
    CHANNEL_IOC_SET_WIEGAND_GPIO    : 8,
    /* Set the delay time for the Wiegand channel. */
    CHANNEL_IOC_SET_WIEGAND_DELAY   : 9,
    /* Configure the logging function of the Wiegand channel. */
    CHANNEL_IOC_SET_WIEGAND_LOG     : 10

};

/* Different working modes of Wiegand channels */
uart.WIEGAND_MODE = {
    /* Wiegand pattern initialization value */
    WIEGAND_MODE_INIT      : 0,
    /* Wiegand 26-bit mode */
    WIEGAND_MODE_26		  : 1,
    /* Wiegand 34-bit mode */
    WIEGAND_MODE_34		  : 2,
    /* Wiegand 128-bit mode */
    WIEGAND_MODE_128       : 3,
    /* Wiegand 256-bit mode */
    WIEGAND_MODE_256       : 4,
    /* Wiegand 2048-bit mode */
    WIEGAND_MODE_2048      : 5,
    /* Custom Wiegand pattern, maximum transmission 6400 bits. */
    WIEGAND_MODE_CUSTOM    : 6
};

/**
 * Open channel
 * @param {number} type Channel type, refer to the enumeration TYPE, required field.
 * @param {string} path Different devices or different types of channels on the same device may correspond to different paths. For example, the value corresponding to path 485 on DW200 is "/dev/ttyS2", which is required.
 * @param {string} id Handle ID, optional (a unique ID is required if opening multiple instances).
 */
uart.open = function (type, path, id) {
	if (type === undefined || type === null) {
		throw new Error("uart.open:'type' should not be null or empty")
	}
	if (path === undefined || path === null) {
		throw new Error("uart.open:'path' should not be null or empty")
	}

	let pointer = uartObj.open(type, path);

	if (pointer === undefined || pointer === null) {
		throw new Error("uart.open: open failed")
	}

	dxCommon.handleId("uart", id, pointer)
}

/**
 * Channel data transmission
 * @param {ArrayBuffer} buffer The data to be sent is required.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
uart.send = function (buffer, id) {
	if (buffer === undefined || buffer === null) {
		throw new Error("uart.send: 'buffer' should not be null or empty")
	}
	let pointer = dxCommon.handleId("uart", id)

	return uartObj.send(pointer, buffer);
}

/**
 * Channel data transmission uses the low-light communication protocol format.
 * @param {string/object} data The data to be sent is required.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
uart.sendVg = function (data, id) {
	if (!data) {
        return
    }
    if (typeof data === 'string') {
        uart.send(dxCommon.hexStringToArrayBuffer(data), id)
        return
    }
    let pack = '55aa' + data.cmd
    if (data.hasOwnProperty('result')) {
        pack += data.result
    }
    pack += (data.length % 256).toString(16).padStart(2, '0')
    pack += (Math.floor(data.length / 256)).toString(16).padStart(2, '0')
    pack += data.data
    let all = dxCommon.hexToArr(pack)
    let bcc = dxCommon.calculateBcc(all)
    all.push(bcc)
    uart.send(new Uint8Array(all).buffer, id)
}

/**
 * To receive data, you need to poll within a thread to retrieve it, and it returns a Uint8Array type.
 * If the received data is less than the specified size, the system will continue to wait until the specified size is received. However, if the timeout is very short, the operation may terminate before all data is received.
 * @param {number} size Number of bytes of data received (required)
 * @param {number} timeout This function will block for a maximum of this time (milliseconds). It will also terminate if `size` data items are received before the timeout period. It is optional and defaults to 10ms.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns The `byteLength` returned by `Uint8Array` indicates the length of the received data; a value of 0 indicates that no data was received.
 */
uart.receive = function (size, timeout, id) {
	if (size === undefined || size === null) {
		throw new Error("uart.receive:'size' should not be null or empty")
	}

	if (timeout === undefined || timeout === null) {
		timeout = 10
	}

	let pointer = dxCommon.handleId("uart", id)

	let res = uartObj.receive(pointer, size, timeout)
	if (res === null) {
		return null
	}
	return new Uint8Array(res)
}

/**
 * Call the channel special I/O interface
 * @param {*} request 
 * @param {*} arg 
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
uart.ioctl = function (request, arg, id) {
	let pointer = dxCommon.handleId("uart", id)

	return uartObj.ioctl(pointer, request, arg)
}

/**
 * Close the channel
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
uart.close = function (id) {
	let pointer = dxCommon.handleId("uart", id)

	return uartObj.close(pointer)
}


/**
 * Refresh Channel
 * @param {number} queue_selector Required
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
uart.flush = function (queue_selector, id) {
	if (queue_selector == null) {
		throw new Error("queue_selector should not be null or empty")
	}
	let pointer = dxCommon.handleId("uart", id)

	return uartObj.flush(pointer, queue_selector);
}


uart.VG = {
	RECEIVE_MSG: '__uartvg__MsgReceive',
}

/**
 * Simplify the use of low-light communication protocols.
 * 1. Receive data: After receiving the binary data from the TLV, parse it into an object and send it out as an event of the eventbus (uart.VG.RECEIVE_MSG+options.id).
 * The returned object format is: {cmd:"2a",result:"01",length:7,data:"0a1acc320fee32",bcc:true}
 * cmd: A 1-byte command word, a hexadecimal string.
 * result: A 1-byte identifier indicating the result of data processing, such as success, failure, or other status. This identifier (a hexadecimal string) is only present in feedback data.
 * length：The length of the data is defined in TLV using 2 bytes, which is directly converted to a decimal number here.
 * data：Multiple-byte data fields, hexadecimal strings
 * bcc: BCC verification successful or failed.
 * 2. Sending data: Convert the object into TLV format binary data before sending it. You can do this using `uart.sendVg('data to be sent', id)`. The data format is as follows:
 * There are two data formats for sending: 1. Object format: {cmd:"2a",result:"01",length:7,data:"0a1acc320fee32"} 2. Complete hexadecimal string '55AA09000000F6'
 * 3. Even with the same ID, runningvg will only execute once even after multiple calls.
 * 
 * @param {object} options Startup parameters
 *			@param {number} options.type Channel type, refer to the enumeration TYPE, required (compatible with USBHID block transfer, default 1024 blocks).
 *			@param {string} options.path Different devices or different types of channels on the same device may correspond to different paths. For example, the value corresponding to path 485 on DW200 is "/dev/ttyS2", which is required.
 *			@param {number} options.result 0 and 1 (default is 0) indicate whether the received data or the transmitted data contains the identifier byte. 0 means the received data does not include the identifier byte, and the transmitted data does; 1 means the opposite.
 *			@param {number} options.passThrough If passThrough is set to true, the received data will use the pass-through mode. This field is not required.
 *          @param {string} options.id  Handle ID, optional (a unique ID is required if initializing multiple instances).
 */
uart.runvg = function (options) {
	if (options === undefined || options.length === 0) {
		throw new Error("dxuart.runvg:'options' parameter should not be null or empty")
	}
	if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
        // handle id
        options.id = ""
    }
	if (options.type === undefined || options.type === null) {
		throw new Error("dxuart.runvg:'type' should not be null or empty")
	}
	if (options.path === undefined || options.path === null) {
		throw new Error("dxuart.runvg:'path' should not be null or empty")
	}
	let oldfilepre = '/app/code/dxmodules/vgUartWorker'
	let content = std.loadFile(oldfilepre + '.js').replace("{{id}}", options.id)
	let newfile = oldfilepre + options.id + '.js'
	std.saveFile(newfile, content)
	let init = map.get("__vguart__run_init" + options.id)
	if (!init) {//Ensure initialization only once
		map.put("__vguart__run_init" + options.id, options)
		bus.newWorker(options.id || "__uart",newfile)
	}
}
export default uart;